package phototagger;

import java.io.*;
import java.util.*;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.parsers.*;
import javax.xml.transform.TransformerException;

import org.w3c.dom.*;

public class TaggerServlet extends HttpServlet {

	{
		System.setProperty("javax.xml.parsers.DocumentBuilderFactory","org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
		System.setProperty("javax.xml.parsers.SAXParserFactory","org.apache.xerces.jaxp.SAXParserFactoryImpl");
	}

	public void init(ServletConfig servletconfig) throws ServletException {    
        super.init(servletconfig);
    }
    
    public void doGet(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse) throws IOException {
            doPost(httpservletrequest, httpservletresponse);
    }
   
    public void doPost(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse) throws IOException {
    	httpservletresponse.setContentType("text/xml");
    	PrintStream out = new PrintStream(httpservletresponse.getOutputStream());
    	String point = httpservletrequest.getParameter("point");
    	int numTags = 5;
    	try { numTags = new Integer(httpservletrequest.getParameter("maxtags")); } catch ( Exception ex ) { }
        try {
        	double longitude = new Double(point.substring(point.indexOf('(')+1,point.indexOf(' ')).trim());
        	double latitude = new Double(point.substring(point.indexOf(' ')+1,point.indexOf(')')).trim());
        	double radius = 32;
        	Document docResponse = makeRecommendations(latitude, longitude, radius, numTags);
            out.println(QueryFlickr.printXML(docResponse));
        } catch ( Exception ex ) {
            Document docResponse = null;
            try {
            	docResponse = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
            } catch ( ParserConfigurationException pex ) { }
            docResponse.appendChild(docResponse.createElement("error"));
            StringWriter msg = new StringWriter();
            ex.printStackTrace(new PrintWriter(msg));
            msg.close();
            docResponse.getDocumentElement().setTextContent(msg.toString());
            try {
            	out.println(QueryFlickr.printXML(docResponse));
            } catch ( TransformerException tex ) { }
        }
    }
    
    public static Document makeRecommendations ( double latitude, double longitude, double radius, int numTags ) throws Exception {
    	if ( Class.forName("org.apache.xerces.parsers.StandardParserConfiguration") != null) {
    		System.setProperty("org.apache.xerces.xni.parser.XMLParserConfiguration", "org.apache.xerces.parsers.StandardParserConfiguration");
    	}
    	Document doc = QueryFlickr.queryFlickr(longitude, latitude, radius);    	
    	String point = "POINT(" + longitude + " " + latitude + ")";
    	final Map<String,String[]> tagsToPhotos = QueryFlickr.tagsToPhotos(doc);
    	final Map<String,String[]> tagsToUsers = QueryFlickr.tagsToUsers(doc);
    	final Map<String,String[]> tagsToPoints = QueryFlickr.tagsToPoints(doc);
    	final Map<String,Integer> tagsToViews = QueryFlickr.tagsToViews(doc);
    	final Map<String,Double> tagsToMinDistance = new HashMap<String,Double>();
    	final Map<String,Double> tagsToMaxDistance = new HashMap<String,Double>();
    	final Map<String,Double> tagsToAvgDistance = new HashMap<String,Double>();
    	for ( String tag : tagsToPhotos.keySet() ) {
    		double numPhotos = tagsToPhotos.get(tag).length;
    		double numUsers = tagsToUsers.get(tag).length;
    		double numViews = tagsToViews.get(tag);
    		double sumGeoSim = 0.0;
    		double minGeoSim = Double.MAX_VALUE;
    		double maxGeoSim = Double.MIN_VALUE;
    		for ( String spoint : tagsToPoints.get(tag) ) {
    			double geoSim = QueryFlickr.distance(spoint,point);
    			minGeoSim = ( geoSim < minGeoSim) ? geoSim : minGeoSim;
    			maxGeoSim = ( geoSim > maxGeoSim) ? geoSim : maxGeoSim;
    			sumGeoSim += geoSim;
    		}
    		double avgGeoSim = sumGeoSim / numPhotos;
    		tagsToMinDistance.put(tag, minGeoSim);
    		tagsToMaxDistance.put(tag, maxGeoSim);
    		tagsToAvgDistance.put(tag, avgGeoSim);
    	}
    	Map<String,String[]> photosToTags = QueryFlickr.photosToTags(doc);    	
    	Map<String,Map<String,Double>> matrix = new HashMap<String,Map<String,Double>>();
    	for ( String photo : photosToTags.keySet() ) {
    		for ( String tag : photosToTags.get(photo) ) {
    			Map<String,Double> aux = matrix.get(photo);
    			if ( aux == null ) aux = new HashMap<String,Double>();
    			aux.put(tag, tagsToAvgDistance.get(tag));
    			matrix.put(photo, aux);
    		}
    	}
    	final Map<String,Double> tagsToScores = SlopeOne.predict(matrix, tagsToAvgDistance);
    	//final Map<String,Double> tagsToScores = tagsToAvgDistance;
    	List<String> tags = new ArrayList<String>();
    	tags.addAll(tagsToScores.keySet());
    	Collections.sort(tags, new Comparator<String>() {
    		public int compare(String o1, String o2) {
    	    	return tagsToScores.get(o2).compareTo(tagsToScores.get(o1));
    	    }
    	});
    	Document docResponse = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
        docResponse.appendChild(docResponse.createElement("tags"));
        for ( int i = 0; i < tags.size() && i < numTags ; i++ ) {
        	String tag = tags.get(i);
        	Element tagElement = docResponse.createElement("tag");
        	tagElement.setAttribute("score", "" + tagsToScores.get(tag));
        	tagElement.setAttribute("num-photos", "" + tagsToPhotos.get(tag).length);
        	tagElement.setAttribute("num-users", "" + tagsToUsers.get(tag).length);
        	tagElement.setAttribute("num-views", "" + tagsToViews.get(tag));
        	tagElement.setAttribute("min-geo-sim", "" + tagsToMinDistance.get(tag));
        	tagElement.setAttribute("max-geo-sim", "" + tagsToMaxDistance.get(tag));
        	tagElement.setAttribute("avg-geo-sim", "" + tagsToAvgDistance.get(tag));
        	tagElement.setAttribute("num-views", "" + tagsToViews.get(tag));
        	tagElement.setTextContent(tag);
        	docResponse.getDocumentElement().appendChild(tagElement);
        }
        return docResponse;
    }
    
    public static void main ( String args[] ) throws Exception {
    	Document doc = makeRecommendations ( 48.83, -2.35, 32, 10);
    	QueryFlickr.printXML(doc);
    }
    
}